package image

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"

	"gitee.com/smewl13/registry-sync/v2/logger"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"golang.org/x/net/context"
)

func PullImg(cli *client.Client, ctx context.Context, username, password, imgurl string) error {
	err := connRegistry(cli, ctx, username, password, imgurl, "pull")
	return err
}

func PushImg(cli *client.Client, ctx context.Context, username, password, image string) error {
	defer DeleteImage(cli, ctx, image)
	err := connRegistry(cli, ctx, username, password, image, "push")
	return err
}

func DeleteImage(cli *client.Client, ctx context.Context, image string) {
	_, err := cli.ImageRemove(ctx, image, types.ImageRemoveOptions{})
	if err != nil {
		logger.Errorf("delete %s err: %v", image, err)
		return
	}
	logger.Infof("delete %s ok", image)
}

func RenameTag(cli *client.Client, ctx context.Context, sourceImage, targetImage string) error {
	if err := cli.ImageTag(ctx, sourceImage, targetImage); err != nil {
		logger.Errorf("tagError: %v", err)
		return err
	}
	return nil
}

func GetImageInfo(cli *client.Client, ctx context.Context, image string) *types.ImageInspect {
	inspect, _, err := cli.ImageInspectWithRaw(ctx, image)
	if err != nil {
		return nil
	}
	return &inspect
}

func DockerCli() (*client.Client, error) {
	cli, err := client.NewClientWithOpts(client.WithVersion("1.39"))
	if err != nil {
		logger.Errorf("clientError: %v", err)
		return cli, err
	}
	return cli, err
}

func connRegistry(cli *client.Client, ctx context.Context, username, password, imgurl, method string) error {
	authConfig := types.AuthConfig{
		Username: username,
		Password: password,
	}
	encodedJSON, err := json.Marshal(authConfig)
	if err != nil {
		logger.Errorf("authError: %v", err)
		return err
	}
	authStr := base64.URLEncoding.EncodeToString(encodedJSON)

	var reader io.ReadCloser
	if method == "pull" {
		reader, err = cli.ImagePull(ctx, imgurl, types.ImagePullOptions{RegistryAuth: authStr})
	} else if method == "push" {
		reader, err = cli.ImagePush(ctx, imgurl, types.ImagePushOptions{RegistryAuth: authStr})
	} else {
		err = fmt.Errorf("镜像请求方法错误。目前只支持'pull'、'push'，当前请求方法: %v", method)
	}

	if err != nil {
		logger.Errorf("请求失败， err: %v", err)
		return err
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(reader)

	for _, line := range bytes.Split(buf.Bytes(), []byte{'\n'}) {
		logger.Debug(string(line))
		m := make(map[string]interface{})
		if len(line) == 0 {
			continue
		}
		if err := json.Unmarshal(line, &m); err != nil {
			logger.Errorf("%s 转换失败, err: %v", string(line), err)
			continue
		}

		if v, ok := m["error"]; ok {
			return fmt.Errorf("%s", v)
		}
	}

	return err
}
